package com.zhaungjie.base.utils.json;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;
import java.util.function.Consumer;


/**
 * ObjectMapper生成器
 *
 * @author zhuangjie
 * @date 2024/08/12
 */
public class ObjectMapperBuilder {

    /**
     * 现成的ObjectMapper
     */
    public static class ReadyMade {
        /**
         * 常见的简单类ObjectMapper
         */
        public static final ObjectMapper BASE_OM; // == ANY_OBJECT_TYPE_OM

        /**
         * 前端适应 && 宽松的Object类型
         */
        public static final ObjectMapper FOR_JS_OM = ObjectMapperBuilder.newInstanceWrapper()
                .ignoreEmptyBeans()
                .ignoreUnknownProperties()
                .ignoreNullProperties()
                .timeZoneGMT8()
                .longToString()
                .prettyPrinter()
                .objectTypeDemand(ObjectMapperBuilder.ObjectMapperBuilderPipeline.ObjectTypeDemand.ANY)
                .build();

        /**
         * 宽松的Object类型（通用常用的）
         * Object序列化前类型 可不等于=> 反序列化后的Object真实类型
         */
        public static final ObjectMapper ANY_OBJECT_TYPE_OM = ObjectMapperBuilder.newInstanceWrapper()
                .ignoreEmptyBeans()
                .ignoreUnknownProperties()
                .ignoreNullProperties()
                .timeZoneGMT8()
                .prettyPrinter()
                .objectTypeDemand(ObjectMapperBuilderPipeline.ObjectTypeDemand.ANY)
                .build();

        /**
         * 明确的Object类型
         * （注意：在序列化时会对类型一同序列化，以便在反序列化时准确解析）
         */
        public static final ObjectMapper CLEARLY_DEFINED_OBJECT_TYPE_OM = ObjectMapperBuilder.newInstanceWrapper()
                .ignoreEmptyBeans()
                .ignoreUnknownProperties()
                .ignoreNullProperties()
                .timeZoneGMT8()
                .longToString()
                .prettyPrinter()
                .objectTypeDemand(ObjectMapperBuilderPipeline.ObjectTypeDemand.CLEARLY_DEFINED)
                .build();

        static {
            BASE_OM = ANY_OBJECT_TYPE_OM;
        }
    }



    public static class ObjectMapperBuilderPipeline {

        /**
         * Object类型在序列化与反序列化时的需求
         */
        public static enum ObjectTypeDemand {
            /**
             * 明确的：会在序列化时将类型一同序列化，以便反序列化时使用
             */
            CLEARLY_DEFINED,
            /**
             * 任意的：不会对类型进行序列化
             */
            ANY;
        }

        /**
         * 默认日期时间格式
         */
        private static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
        /**
         * 默认日期格式
         */
        private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
        /**
         * 默认时间格式
         */
        private static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";


        private ObjectMapper objectMapper;

        private JavaTimeModule javaTimeModule = new JavaTimeModule();

        public ObjectMapperBuilderPipeline() {
            objectMapper = new ObjectMapper();
            objectMapper.registerModule(javaTimeModule);
            // 在这里设置默认的
            // 指定时区
            objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
            // 日期类型字符串处理
            objectMapper.setDateFormat(new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT));
            // 支持java8日期日期类型处理
            this.superJava8TimeModule();
            // 忽略特殊方法转为属性
            ignoreMethodToJsonField();
        }

        /**
         * 支持java8日期日期类型处理
         */
        private void superJava8TimeModule() {
            javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
            javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
            javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
            javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
            javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
            javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        }

        /**
         * 取消方法序列化为属性
         */
        private ObjectMapperBuilderPipeline ignoreMethodToJsonField() {
            objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
            objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
            return this;
        }

        /**
         * 忽略无法转换的对象
         */
        public ObjectMapperBuilderPipeline ignoreEmptyBeans() {
            objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
            return this;
        }

        /**
         * 忽略json字符串中不识别的属性
         */
        public ObjectMapperBuilderPipeline ignoreUnknownProperties() {
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            return this;
        }

        /**
         * PrettyPrinter 格式化输出
         */
        public ObjectMapperBuilderPipeline prettyPrinter() {
            objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
            return this;
        }

        /**
         * NULL不参与序列化
         */
        public ObjectMapperBuilderPipeline ignoreNullProperties() {
            objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            return this;
        }

        /**
         * 指定时区
         */
        public ObjectMapperBuilderPipeline timeZone(TimeZone timeZone) {
            objectMapper.setTimeZone(timeZone);
            return this;
        }

        /**
         * 指定时区为GMT+8:00
         */
        public ObjectMapperBuilderPipeline timeZoneGMT8() {
            return this.timeZone(TimeZone.getTimeZone("GMT+8:00"));
        }

        /**
         * 日期类型字符串处理
         * 使用DEFAULT_DATE_TIME_FORMAT
         * 自定义日期类型字符串处理
         */
        public ObjectMapperBuilderPipeline dateFormat(String dateFormat) {
            objectMapper.setDateFormat(new SimpleDateFormat(dateFormat));
            return this;
        }

        /**
         * 处理Long类型，返回给前端的应是String类型
         * 场景：将使用String来序列化Long类型(雪花算法，前端js会整形会丢失精度问题，所以需要转字符串，在反序列时String会自动转Long类型)
         */
        public ObjectMapperBuilderPipeline longToString() {
            javaTimeModule.addSerializer(Long.class, ToStringSerializer.instance);
            javaTimeModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
            objectMapper.registerModule(javaTimeModule);
            return this;
        }

        /**
         * 对Object类型的需求/松紧度
         * 为解决java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to
         */
        public ObjectMapperBuilderPipeline objectTypeDemand(ObjectTypeDemand objectTypeDemand) {
            switch (objectTypeDemand) {
                case ANY:
                    // 允许 Object 属性的值为任意类型
                    objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
                    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
                    // 设置 Object 属性不要求具体类型
                    objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
                    objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true);
                    break;
                case CLEARLY_DEFINED:
                    objectMapper.activateDefaultTyping(
                            objectMapper.getPolymorphicTypeValidator(),
                            ObjectMapper.DefaultTyping.NON_FINAL,
                            JsonTypeInfo.As.PROPERTY);
            }
            return this;
        }

        /**
         * 对Object类型的需求/松紧度
         * 默认值：ANY （宽松的），表示反序列时，Object原序列化前与现在反序列后要求不要求一致，可任意
         * @return
         */
        public ObjectMapperBuilderPipeline objectTypeDemand() {
            return this.objectTypeDemand(ObjectTypeDemand.ANY);
        }

        /**
         * 自定义 设置objectMapper
         *
         * @param objectMapperConsumer
         * @return
         */
        public ObjectMapperBuilderPipeline custom(Consumer<ObjectMapper> objectMapperConsumer) {
            objectMapperConsumer.accept(objectMapper);
            return this;
        }

        public ObjectMapper build() {
            return objectMapper;
        }

    }

    public static ObjectMapperBuilderPipeline newInstanceWrapper() {
        return new ObjectMapperBuilderPipeline();
    }


}
